package com.zyx.disruptor.falsesharing;

/**
 * @author Yaxi.Zhang
 * @since 2021/12/13 09:59
 * desc: 伪共享
 * 什么是伪共享
 *  以ArrayBlockingQueue为例, 它有三个成员变量:
 *      - takeIndex: 需要被取走的元素下标
 *      - putIndex: 可被元素插入的位置的下标
 *      - count: 队列中元素的数量
 *  这三个变量很容易放到一个缓存行中, 但是之间修改没有太多的关联, 所以每次修改, 都会使之前缓存的数据失效, 从而不能完全达到共享的效果
 * 这种无法充分使用缓存行特性的现象, 称为伪共享
 * 对于伪共享, 一般的解决方案是, 增大数组元素的间隔使得由不同线程存取的元素位于不同的缓存行上, 以空间换时间
 */
public class FalseSharing implements Runnable {
    public final static long ITERATIONS = 500L * 1000L * 100L;
    private int arrayIndex = 0;

    private static ValuePadding[] longsPadding;
    private static ValueNoPadding[] longsNoPadding;


    public FalseSharing(final int arrayIndex) {
        this.arrayIndex = arrayIndex;
    }

    public static void main(final String[] args) throws Exception {
        for (int i = 1; i < 10; i++) {
            System.gc();
            final long start = System.currentTimeMillis();
            // runTestPadding(i);
            runTestNoPadding(i);
            System.out.println("Thread num " + i + " duration = " + (System.currentTimeMillis() - start));
        }

    }

    private static void runTestPadding(int NUM_THREADS) throws InterruptedException {
        Thread[] threads = new Thread[NUM_THREADS];
        longsPadding = new ValuePadding[NUM_THREADS];
        for (int i = 0; i < longsPadding.length; i++) {
            longsPadding[i] = new ValuePadding();
        }
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new FalseSharing(i));
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            t.join();
        }
    }

    private static void runTestNoPadding(int NUM_THREADS) throws InterruptedException {
        Thread[] threads = new Thread[NUM_THREADS];
        longsNoPadding = new ValueNoPadding[NUM_THREADS];
        for (int i = 0; i < longsNoPadding.length; i++) {
            longsNoPadding[i] = new ValueNoPadding();
        }
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(new FalseSharing(i));
        }

        for (Thread t : threads) {
            t.start();
        }

        for (Thread t : threads) {
            t.join();
        }
    }

    @Override
    public void run() {
        long i = ITERATIONS + 1;
        while (0 != --i) {
            // longsPadding[arrayIndex].value = 0L;
            longsNoPadding[arrayIndex].value = 0L;
        }
    }

    /**
     * ValueNoPadding[] 数组元素间隔大, 数组中不同元素位于不同 cache line 上
     */
    public final static class ValuePadding {
        protected long p1, p2, p3, p4, p5, p6, p7;
        protected volatile long value = 0L;
        protected long p9, p10, p11, p12, p13, p14;
        protected long p15;
    }

    /**
     * ValueNoPadding[] 数组元素的间隔较小, 容易位于相同的缓存行上
     * 导致修改数组中的某个元素使得整个cache line被修改, 从而造成伪共享
     */
    public final static class ValueNoPadding {
        // protected long p1, p2, p3, p4, p5, p6, p7;
        protected volatile long value = 0L;
        // protected long p9, p10, p11, p12, p13, p14, p15;
    }
}
